/*
 * StringTemplateLayoutServlet.java
 *
 * Copyright 2010 Jack Leow
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package my.edu.clhs.stringtemplate.servlet.view;

import java.io.IOException;
import java.io.ObjectInputStream;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;

import my.edu.clhs.stringtemplate.servlet.ServletContextStringTemplateGroupLoader;
import my.edu.clhs.stringtemplate.servlet.StringTemplateSupport;

import org.antlr.stringtemplate.StringTemplate;
import org.antlr.stringtemplate.StringTemplateGroup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Servlet for presenting content using StringTemplate as the templating engine
 * with a pre-defined layout.
 *
 * <p>With this implementation all pages share a common layout, defined in the
 * StringTemplate file specified by <tt>layoutTemplatePath</tt>.
 *
 * <p>Each page has its own StringTemplateGroup (.stg) file in one of the
 * colon-delimited paths in the <tt>templateRootPaths</tt> init-param. The
 * template group for a request is resolved using its servletPath and pathInfo,
 * with its extension stripped and replaced with <tt>.stg</tt>.
 *
 * <p><b><a name="init-params">Configuration <tt>init-param</tt>s</a>
 * (<a href="AbstractStringTemplateServlet.html#init-param">
 * In addition to the superclass</a>)</b>:
 * <table border="1">
 *  <tr>
 *      <th>param-name</th>
 *      <th>Description</th>
 *  </tr>
 *  <tr>
 *      <td>layoutTemplatePath</td>
 *      <td>
 *          The path, relative to the web application context, to the layout
 *          template file.
 *          Defaults to <tt>/WEB-INF/stringtemplate/layout.st</tt>.
 *      </td>
 *  </tr>
 *  <tr>
 *      <td>templateRootPaths</td>
 *      <td>
 *          A colon separated list of paths, relative to the web application
 *          context, to the directories containing the template group files.
 *          Defaults to <tt>/WEB-INF/stringtemplate</tt>.
 *      </td>
 *  </tr>
 *  <!--
 *  <tr>
 *      <td></td>
 *      <td>
 *      </td>
 *  </tr>
 *  -->
 * </table>
 *
 * @author Jack Leow
 * @version 1.0
 * @since 1.0 (Feb 20, 2010, 9:45:09 AM)
 */
public class StringTemplateLayoutServlet extends AbstractStringTemplateServlet {
    public static final String DEFAULT_LAYOUT_TEMPLATE_PATH =
        "/WEB-INF/stringtemplate/layout";
    public static final String DEFAULT_TEMPLATE_ROOT_PATHS =
        "/WEB-INF/stringtemplate";
    
    private static final String LAYOUT_TEMPLATE_PATH_PARAM =
        "layoutTemplatePath";
    private static final String TEMPLATE_ROOT_PATHS_PARAM =
        "templateRootPaths";
    
    private static final Logger log =
        LoggerFactory.getLogger(StringTemplateLayoutServlet.class);
    private static final long serialVersionUID = 1L;
    
    private transient
        ServletContextStringTemplateGroupLoader templateGroupLoader;
    
    protected ServletContextStringTemplateGroupLoader getTemplateGroupLoader() {
        return templateGroupLoader;
    }
    
    private void initTemplateGroupLoader() {
        templateGroupLoader = new ServletContextStringTemplateGroupLoader(
            this.templateRootPaths, getServletContext());
        String templateFileEncoding = getTemplateFileEncoding();
        if (templateFileEncoding != null) {
            templateGroupLoader.setFileCharEncoding(templateFileEncoding);
        }
    }
    
    private String layoutTemplatePath = DEFAULT_LAYOUT_TEMPLATE_PATH;
    
    protected String getLayoutTemplatePath() {
        return layoutTemplatePath;
    }
    
    private String templateRootPaths = DEFAULT_TEMPLATE_ROOT_PATHS;
    
    protected String getTemplateRootPaths() {
        return templateRootPaths;
    }
    
    // @Override
    public void init() throws ServletException {
        log.debug("entering init()");
        super.init();
        ServletConfig config = getServletConfig();
        
        String layoutTemplateName =
            config.getInitParameter(LAYOUT_TEMPLATE_PATH_PARAM);
        if (layoutTemplateName != null) {
            this.layoutTemplatePath = layoutTemplateName;
        }
        
        String templateRootPaths =
            config.getInitParameter(TEMPLATE_ROOT_PATHS_PARAM);
        if (templateRootPaths != null) {
            this.templateRootPaths = templateRootPaths;
        }
        
        initTemplateGroupLoader();
        StringTemplateGroup.registerGroupLoader(templateGroupLoader);
        log.debug("exiting init()");
    }
    
    /**
     * Loads the layout template, using the templateName to resolve the content
     * template group.
     */
    // @Override
    protected StringTemplate loadTemplate(String templateName) {
        log.debug("entering loadTemplate(String)");
        StringTemplate template;
        
        try {
            StringTemplateSupport support = getStringTemplateSupport();
            template = support.loadLayoutTemplate(
                templateName, getLayoutTemplatePath(), getTemplateLoader());
            
            if (template != null) {
                support.appendServletUtilTemplateGroup(template.getGroup());
            }
        } catch (IllegalArgumentException e) {
            if (!e.getMessage().startsWith("Can't find template")) {
                throw e;
            }
            IllegalStateException ise = new IllegalStateException(
                "layout specified by \"layoutTemplatePath\" init-param " +
                "does not exist");
            ise.initCause(e);
            throw ise;
        }
        
        log.debug("exiting loadTemplate(String)");
        return template;
    }
    
    private void readObject(ObjectInputStream in)
            throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        initTemplateGroupLoader();
    }
}